using System;
using System.Data;
using log4net;
using NHibernate;
using NhJump.Persistence.Batching;

namespace NhJump.Persistence.Context
{
    public interface IUnitOfWork : IDisposable
    {
        ISession Session { get; }
        IBatcher Batcher { get; }
        void Rollback();
        void Commit();
    }

    public class UnitOfWork : IUnitOfWork
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(UnitOfWork));
        public UnitOfWork(ISessionFactory factory)
            : this(factory, null)
        {
        }

        public UnitOfWork(ISessionFactory factory, IDbConnection dbConnection)
        {
            Logger.Debug("Constructing UnitOfWork");
            Session = dbConnection!=null 
                          ? factory.OpenSession(dbConnection) 
                          : factory.OpenSession();
  
            Transaction = Session.BeginTransaction();

            Batcher = new Batcher(new MultiCriteriaBatchExecuter(Session));
        }

        private ITransaction Transaction { get; set;}
        public ISession Session { get; private set; }
        public IBatcher Batcher { get; private set; }

        public virtual void Dispose()
        {
            if (IsTransactionOpen())
            {
                Rollback();
            }

            if (Transaction != null)
            {
                Transaction.Dispose();
            }

            if (Session != null)
            {
                Session.Dispose();
            }
        }

        public void Rollback()
        {
            if (IsTransactionOpen())
            {
                Transaction.Rollback();
            }
        }

        private bool IsTransactionOpen()
        {
            return Transaction != null
                   && Transaction.IsActive
                   && ! Transaction.WasRolledBack 
                   && ! Transaction.WasCommitted;
        }

        public void Commit()
        {
            if (IsTransactionOpen())
            {
                Transaction.Commit();
            }
        }
    }
}